#include "alu.h"

int neg(int word) {
	word ^= SIGN_BIT_MASK; // reverse the sign bit
	return clr(word);
}

int abso(int word) {
	word &= ~SIGN_BIT_MASK; // clear the sign bit
	return clr(word);
}

int clr(int word) {
	word &= ~OVER_BIT_MASK; // clear the overflow bit
	if(word == SIGN_BIT_MASK)
		word = 0; // make -0 become +0
	return word;
}

int add(int wordA, int wordB) {
	int ret;
	int absWordA = abso(wordA);
	int absWordB = abso(wordB);
	if((wordA ^ wordB) >= 0) {
		// both 'wordA' and 'wordB' have the same sign bit
		ret = absWordA + absWordB;
		ret |= wordA & SIGN_BIT_MASK; // assign the sign bit
	}
	else if(absWordA >= absWordB) {
		ret = absWordA - absWordB;
		ret |= wordA & SIGN_BIT_MASK;
	}
	else {
		ret = absWordB - absWordA;
		ret |= wordB & SIGN_BIT_MASK;
	}
	return ret;
}

int sub(int wordA, int wordB) {
	return add(wordA, neg(wordB)); // a - b = a + (-b)
}

long long imul(int wordA, int wordB) {
	long long ret = (long long)abso(wordA) * abso(wordB); // __HL
	ret <<= 2; // _HL_
	*(unsigned int*)(&ret) >>= 2; // _H_L
	if((wordA ^ wordB) < 0) {
		int *p = (int*)(&ret);
		for(int i = 0; i < 2; ++i)
			p[i] |= SIGN_BIT_MASK; // assign the sign bit
	}
	return ret;
}

long long idiv(int wordA, int wordX, int wordB) {
	long long dividend = abso(wordA); // _A
	dividend <<= 30; // _A_
	dividend |= abso(wordX); // _AX
	long long ret = dividend / abso(wordB); // _(AX / B)
	if(ret >= OVER_BIT_MASK) {
		ret &= OVER_BIT_MASK - 1; // only save the low 30 bits
		ret |= OVER_BIT_MASK; // set the overflow bit
	}
	ret <<= 32; // _(AX / B)_
	ret |= dividend % abso(wordB); // _(AX / B)_(AX % B)
	if((wordA ^ wordB) < 0) 
		ret |= (SIGN_BIT_MASK * 1LL) << 32; // assign the sign bit for quotient
	// assign the sign bit for remainder
	ret |= wordA & SIGN_BIT_MASK;
	return ret;
}

int shl(int word, int count) {
	count = clr(count);
	if(count < 0)
		return -1; // undefined behavior
	if(count >= 5)
		return 0;
	int ret = abso(word) << (count * 6); // 6 bits per word in the mixal
	ret = abso(ret); // clear the sign and overflow bit
	ret |= word & SIGN_BIT_MASK; // assgin the sign bit
	return ret;	
}

int shr(int word, int count) {
	count = clr(count);
	if(count < 0)
		return -1; // undefined behavior
	if(count >= 5)
		return 0;
	int ret = abso(word) >> (count * 6); // 6 bits per word in the mixal
	ret |= word & SIGN_BIT_MASK; // assgin the sign bit
	return ret;	
}


int store(int content, int field, int word) {
	long long divRet = idiv(0, field, 8);
	int L = clr(divRet >> 32);
	int R = clr(divRet);	
	if(L < 0 || R < 0)
		return -1; // undefined behavior
	if(L > R || R > 5)
		return -1; // undefined behavior
	int sign = content & SIGN_BIT_MASK;
	if(!L) {
		// record the sign of 'word'
		sign = word & SIGN_BIT_MASK;
		++L;
		if(!R) 
			// assign the sign of 'content' with the sign of 'word'
			return abso(content) | sign;
  }
	// 1 <= L <= R <= 5
	// let's take field (2:4) as example to better explain
	int cLeft, cRight, wRight, ret;
	cLeft = cRight = abso(content); // C1C2C3C4C5
	cLeft = shr(cLeft, 6 - L); // ~~~~C1
	cLeft = shl(cLeft, R - L + 1); // ~C1~~~
	wRight = abso(word);
	wRight = shl(wRight, 4 + L - R); // W3W4W5~~
	wRight = shr(wRight, 4 + L - R); // ~~W3W4W5
	cRight = shl(cRight, R); // C5~~~~
	cRight = shr(cRight, R); // ~~~~C5
	ret = cLeft | wRight; // ~C1W3W4W5
	ret = shl(ret, 5 - R); // C1W3W4W5~
	ret |= cRight; // C1W3W4W5C5
	return ret | sign;
}
